/*
 *    Copyright (C) 2011-2012 Hai Bison
 *
 *    See the file LICENSE at the root directory of this project for copying
 *    permission.
 */

/*
 * PanelProject.java
 *
 * Created on Sep 11, 2011, 10:54:28 AM
 */
package group.pals.mailgate;

import group.pals.mailgate.beans.ItemEmailAccount;
import group.pals.mailgate.beans.ItemEmailMessage;
import group.pals.mailgate.beans.ItemEmailProvider;
import group.pals.mailgate.beans.ItemGroupEmailMessages;
import group.pals.mailgate.beans.Utils;
import group.pals.mailgate.media.SoundPlayer;
import group.pals.mailgate.objects.StringObjectComparator;
import group.pals.mailgate.threads.EmailReader;
import group.pals.mailgate.ui.RightAlignCellRenderer;
import group.pals.mailgate.utils.Initialization;
import group.pals.mailgate.preferences.Preferences;
import group.pals.mailgate.preferences.PreferencesAdapter;
import group.pals.threads.BaseThread;
import group.pals.threads.ThreadManager;
import group.pals.threads.listeners.ThreadAdapter;
import group.pals.utils.math.Converter;
import group.pals.utils.sys.MessageBox;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.mail.Flags.Flag;
import javax.swing.AbstractAction;
import javax.swing.DefaultRowSorter;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.RowSorter;
import javax.swing.SortOrder;
import javax.swing.SwingConstants;
import javax.swing.Timer;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;
import javax.swing.table.TableRowSorter;

/**
 *
 * @author Hai Bison
 */
public class PanelProject extends javax.swing.JPanel {

    /**
     * Instance of {@link PanelProject}
     */
    private final javax.swing.JPanel Instance = this;
    private final Preferences _Preferences;
    private final Utils BeanUtils;
    private final ThreadManager _ThreadManager;
    private final DefaultTableModel TableEmailReadersModel;
    private final DefaultTableModel TableEmailMessagesModel;
    private final Map<String, Integer> MapColsTableEmailReaders;
    private final Map<String, Integer> MapColsTableEmailMessages;
    private final StringObjectComparator DefaultStringObjectComparator;
    private final JPopupMenu PopupMenuTableEmailReaders;
    private final JPopupMenu PopupMenuTableEmailMessages;
    private final DialogPopupMessages PopupMessagesWindow;
    private final SoundPlayer _SoundPlayer;
    private boolean isGroupEmailsByConversation;

    /** Creates new form PanelProject */
    public PanelProject(java.awt.Frame parent) {
        initComponents();

        this._Preferences = Preferences.getInstance();
        this.BeanUtils = Utils.getInstance();
        this._ThreadManager = ThreadManager.getInstance();

        this.DefaultStringObjectComparator = new StringObjectComparator();

        initActions();

        this.PopupMenuTableEmailReaders = new JPopupMenu();
        initPopupMenuTableEmailReaders();

        initTableEmailReaders();
        this.TableEmailReadersModel = (DefaultTableModel) tableEmailReaders.getModel();
        this.MapColsTableEmailReaders = new HashMap<String, Integer>();
        for (String header : TableEmailReadersHeaders) {
            this.MapColsTableEmailReaders.put(header, tableEmailReaders.getColumn(header).getModelIndex());
        }

        this.PopupMenuTableEmailMessages = new JPopupMenu();
        initPopupMenuTableEmailMessages();

        initTableEmailMessages();
        this.TableEmailMessagesModel = (DefaultTableModel) tableEmailMessages.getModel();
        this.MapColsTableEmailMessages = new HashMap<String, Integer>();
        for (String header : TableEmailMessagesHeaders) {
            this.MapColsTableEmailMessages.put(header, tableEmailMessages.getColumn(header).getModelIndex());
        }

        this.PopupMessagesWindow = new DialogPopupMessages(null, false);
        this._SoundPlayer = SoundPlayer.getInstance();
        this._SoundPlayer.setSoundFile(_Preferences.getPlaySoundFile());

        this.isGroupEmailsByConversation = true;

        initUI();
        initProject();
        initInfoTimer();
        initListeners();
    }

    private void initActions() {
        actionRestartAllEmailReaders = new ActionRestartAllEmailReaders();
        actionDoCheckEmails = new ActionDoCheckEmails();

        actionMarkEmailMessagesAsSpam = new ActionMarkEmailMessagesAsSpam();
        actionMarkEmailMessagesAsRead = new ActionMarkEmailMessagesAsRead();
        actionDeleteEmailMessages = new ActionDeleteEmailMessages();
    }

    private void initUI() {
        jSplitPane1.setDividerLocation(150);
        labelInfo.setText(null);
    }

    private void initProject() {
        isGroupEmailsByConversation = _Preferences.isGroupEmailsByConversation();

        TableColumn col = tableEmailMessages.getColumn(ColMessageCount);
        col.setMinWidth(isGroupEmailsByConversation ? ColMessageCountWidth : 0);
        col.setMaxWidth(isGroupEmailsByConversation ? ColMessageCountWidth : 0);
        col.setPreferredWidth(isGroupEmailsByConversation ? ColMessageCountWidth : 0);

        List<ItemEmailAccount> emailAccounts = _Preferences.getEmailAccounts();
        TableEmailReadersModel.setRowCount(0);

        for (ItemEmailAccount emailAccount : emailAccounts) {
            ItemEmailProvider emailProvider = BeanUtils.getEmailProvider(emailAccount);
            if (emailProvider == null) {
                continue;
            }

            EmailReader emailReader = new EmailReader(null, emailAccount, emailProvider);
            _ThreadManager.add(emailReader);

            Object[] row = new Object[TableEmailReadersHeaders.length];
            row[MapColsTableEmailReaders.get(ColEmailReader)] = emailReader;
            TableEmailReadersModel.addRow(row);

            emailReader.setConnectionTimeout(_Preferences.getConnectionTimeout());
            emailReader.setEmailCheckTimePeriod(_Preferences.getEmailCheckTimePeriod());

            emailReader.addThreadListener(new ThreadAdapter() {

                private void setEmailReaderInfo(BaseThread sender, String info) {
                    final int Col = MapColsTableEmailReaders.get(ColEmailReader);

                    //remember current selection
                    EmailReader lastSelectedEmailReader = null;
                    if (tableEmailReaders.getSelectedRowCount() == 1) {
                        lastSelectedEmailReader = (EmailReader) tableEmailReaders.getValueAt(tableEmailReaders.getSelectedRow(), Col);
                    }

                    final SimpleDateFormat Sdf = new SimpleDateFormat("HH:mm:ss");
                    for (int row = 0; row < TableEmailReadersModel.getRowCount(); row++) {
                        if (TableEmailReadersModel.getValueAt(row, Col).equals(sender)) {
                            TableEmailReadersModel.setValueAt(
                                    String.format("[%s] %s", Sdf.format(new Date()), info),
                                    row, MapColsTableEmailReaders.get(ColInfo));
                            TableEmailReadersModel.fireTableDataChanged();

                            //restore last selection
                            if (lastSelectedEmailReader != null) {
                                for (int k = 0; k < TableEmailReadersModel.getRowCount(); k++) {
                                    if (lastSelectedEmailReader.equals(TableEmailReadersModel.getValueAt(k, Col))) {
                                        int lastSelectedRow = tableEmailReaders.convertRowIndexToView(k);
                                        tableEmailReaders.getSelectionModel().setSelectionInterval(lastSelectedRow, lastSelectedRow);
                                        break;
                                    }
                                }
                            }
                            break;
                        }
                    }
                }

                @Override
                public synchronized void threadRaised(BaseThread sender, Exception ex, Object obj) {
                    setEmailReaderInfo(sender, String.format("Exception: %s", ex));
                }

                @Override
                public synchronized void threadSignaled(BaseThread sender, Object message, Object obj) {
                    final EmailReader _EmailReader = (EmailReader) sender;
                    switch ((Integer) message) {
                        case EmailReader.KeyNewEmailMessages: {
                            PopupMessagesWindow.updateEmails(_EmailReader, _EmailReader.cloneEmailMessages());
                            if (_Preferences.isPlaySoundOn()) {
                                _SoundPlayer.play();
                            }
                            showPopupMessages();
                            break;
                        }//KeyNewEmailMessages

                        case EmailReader.KeyManualReport: {
                            PopupMessagesWindow.updateEmails(_EmailReader, _EmailReader.cloneEmailMessages());
                            break;
                        }//KeyManualReport

                        case EmailReader.KeyMessage: {
                            setEmailReaderInfo(sender, obj.toString());
                            break;
                        }//KeyMessage
                    }//case
                }
            });

            emailReader.start();
        }

        TableEmailReadersModel.fireTableDataChanged();
        updateTableEmailReadersRowSorter();
    }//init project

    private void initInfoTimer() {
        final Runtime _Runtime = Runtime.getRuntime();

        new Timer(999, new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                if (!Instance.isVisible()) {
                    return;
                }

                final long MillisecondsOfOneDay = 24 * 60 * 60 * 1000;
                final long ElapsedTime = new Date().getTime() - Initialization.StartTime.getTime();
                String elapsedTime = "<b>Elapsed time:</b>";
                if (ElapsedTime < MillisecondsOfOneDay) {
                    elapsedTime = String.format("%s %s", elapsedTime, Converter.milliSecondToString(ElapsedTime, false));
                } else {
                    final long DayCount = ElapsedTime / MillisecondsOfOneDay;
                    elapsedTime = String.format("%s %,d day%s, %s", elapsedTime, DayCount,
                            DayCount <= 1 ? "" : "s", Converter.milliSecondToString(ElapsedTime % MillisecondsOfOneDay, false));
                }

                labelInfo.setText(String.format("<html><b>Memory:</b> %s (of %s) - <b>Threads:</b> %,d - %s",
                        Converter.sizeToStr(_Runtime.totalMemory() - _Runtime.freeMemory()),
                        Converter.sizeToStr(_Runtime.totalMemory()),
                        _ThreadManager.size(), elapsedTime));
            }
        }).start();
    }

    private void initListeners() {
        _Preferences.addPreferencesListener(new PreferencesAdapter() {

            @Override
            public void preferencesStored() {
                restartProject();
            }
        });
    }

    /**
     * Restarts project.
     */
    public synchronized void restartProject() {
        TableEmailReadersModel.setRowCount(0);
        TableEmailMessagesModel.setRowCount(0);

        _ThreadManager.interruptAll();
        PopupMessagesWindow.clearAllEmailReaders();
        _SoundPlayer.setSoundFile(_Preferences.getPlaySoundFile());

        System.gc();

//    final DialogWaiting dw = new DialogWaiting(null, "Stopping threads...");
//    dw.setTerminationListener(new AbstractAction("Cancel") {
//
//      public void actionPerformed(ActionEvent e) {
//        dw.dispose();
//      }
//    });
//
//    dw.doWait();
//    try {
//      while (!threadManager.isEmpty()) {
//        threadManager.recheck();
//        try {
//          Thread.sleep(200);
//        } catch (InterruptedException ie) {
//          break;
//        }
//      }
//    } finally {
//      dw.dispose();
//    }

        initProject();
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {
        java.awt.GridBagConstraints gridBagConstraints;

        labelInfo = new javax.swing.JLabel();
        jSplitPane1 = new javax.swing.JSplitPane();
        jScrollPane1 = new javax.swing.JScrollPane();
        tableEmailReaders = new javax.swing.JTable();
        jScrollPane2 = new javax.swing.JScrollPane();
        tableEmailMessages = new javax.swing.JTable();

        setLayout(new java.awt.GridBagLayout());

        labelInfo.setFont(new java.awt.Font("Dialog", 0, 12));
        labelInfo.setText("labelInfo");
        labelInfo.setBorder(javax.swing.BorderFactory.createEtchedBorder());
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
        add(labelInfo, gridBagConstraints);

        jSplitPane1.setDividerLocation(200);
        jSplitPane1.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);

        jScrollPane1.setBorder(javax.swing.BorderFactory.createTitledBorder(null, "Accounts:", javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Dialog", 1, 12), java.awt.Color.blue)); // NOI18N

        tableEmailReaders.setModel(new javax.swing.table.DefaultTableModel(
            new Object [][] {

            },
            new String [] {

            }
        ));
        tableEmailReaders.getTableHeader().setReorderingAllowed(false);
        jScrollPane1.setViewportView(tableEmailReaders);

        jSplitPane1.setLeftComponent(jScrollPane1);

        jScrollPane2.setBorder(javax.swing.BorderFactory.createTitledBorder(null, "E-mails:", javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Dialog", 1, 12), java.awt.Color.blue)); // NOI18N

        tableEmailMessages.setModel(new javax.swing.table.DefaultTableModel(
            new Object [][] {

            },
            new String [] {

            }
        ));
        tableEmailMessages.getTableHeader().setReorderingAllowed(false);
        jScrollPane2.setViewportView(tableEmailMessages);

        jSplitPane1.setBottomComponent(jScrollPane2);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.weighty = 1.0;
        add(jSplitPane1, gridBagConstraints);
    }// </editor-fold>//GEN-END:initComponents
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JScrollPane jScrollPane2;
    private javax.swing.JSplitPane jSplitPane1;
    private javax.swing.JLabel labelInfo;
    private javax.swing.JTable tableEmailMessages;
    private javax.swing.JTable tableEmailReaders;
    // End of variables declaration//GEN-END:variables

    /*----------------------------------------------------------------------------
     * Table E-mail Readers
     */
    private final String ColRowNo = "#";
    private final String ColEmailReader = "E-mail";
    private final String ColNewMessageCount = "New Messages";
    private final String ColInfo = "Info";
    private final String[] TableEmailReadersHeaders = {ColRowNo, ColEmailReader, ColNewMessageCount, ColInfo};
    private final Class[] TableEmailReadersHeaderClasses = {String.class,
        EmailReader.class, String.class, String.class};

    //<editor-fold defaultstate="collapsed" desc="table initialization">
    private void initTableEmailReaders() {
        tableEmailReaders.setModel(new javax.swing.table.DefaultTableModel(
                new Object[][]{}, TableEmailReadersHeaders) {

            @Override
            public Class getColumnClass(int columnIndex) {
                return TableEmailReadersHeaderClasses[columnIndex];
            }

            @Override
            public boolean isCellEditable(int rowIndex, int columnIndex) {
                return false;
            }

            @Override
            public Object getValueAt(int row, int column) {
                final String ColName = getColumnName(column);

                if (ColName.equals(ColRowNo)) {
                    return String.format("%,02d", tableEmailReaders.convertRowIndexToView(row) + 1);
                } else if (ColName.equals(ColEmailReader)) {
                    return super.getValueAt(row, column);
                } else {
                    EmailReader emailReader = (EmailReader) super.getValueAt(row, MapColsTableEmailReaders.get(ColEmailReader));

                    if (ColName.equals(ColNewMessageCount)) {
                        return String.format("%,d", emailReader.cloneEmailMessages().size());
                    }

                    return super.getValueAt(row, column);
                }
            }//getValueAt()
        });

        //------------------------ Init columns
        RightAlignCellRenderer rightAlignCellRenderer = new RightAlignCellRenderer();
        rightAlignCellRenderer.setCellHorzAlignment(SwingConstants.RIGHT);

        TableColumn col = tableEmailReaders.getColumn(ColRowNo);
        col.setCellRenderer(rightAlignCellRenderer);
        col.setMinWidth(50);
        col.setMaxWidth(50);

        col = tableEmailReaders.getColumn(ColNewMessageCount);
        col.setCellRenderer(rightAlignCellRenderer);
        col.setMinWidth(150);
        col.setMaxWidth(150);

        //-------------------------- Listeners
        tableEmailReaders.getSelectionModel().addListSelectionListener(new ListSelectionListener() {

            public void valueChanged(ListSelectionEvent e) {
                if (tableEmailReaders.getSelectedRowCount() == 1 && !e.getValueIsAdjusting()) {
                    final int Col = MapColsTableEmailReaders.get(ColEmailReader);
                    EmailReader emailReader = (EmailReader) tableEmailReaders.getValueAt(tableEmailReaders.getSelectedRow(), Col);
                    updateTableEmailMessages(emailReader);
                } else {
                    updateTableEmailMessages(null);
                }
            }
        });

        //-------------------------------- row sorter
        DefaultRowSorter rowSorter = new TableRowSorter(tableEmailReaders.getModel());
        tableEmailReaders.setRowSorter(rowSorter);
        rowSorter.setComparator(tableEmailReaders.getColumn(ColEmailReader).getModelIndex(), DefaultStringObjectComparator);
        rowSorter.setComparator(tableEmailReaders.getColumn(ColInfo).getModelIndex(), DefaultStringObjectComparator);

        //-------------------------------- popup menu
        tableEmailReaders.setComponentPopupMenu(PopupMenuTableEmailReaders);
        jScrollPane1.setComponentPopupMenu(PopupMenuTableEmailReaders);
    }// </editor-fold>

    private void updateTableEmailReadersRowSorter() {
        List sortKeys = tableEmailReaders.getRowSorter().getSortKeys();
        if (sortKeys == null || sortKeys.isEmpty()) {
            sortKeys = new ArrayList<RowSorter.SortKey>();
            sortKeys.add(new RowSorter.SortKey(
                    tableEmailReaders.getColumn(ColEmailReader).getModelIndex(), SortOrder.ASCENDING));
            try {
                tableEmailReaders.getRowSorter().setSortKeys(sortKeys);
            } catch (Exception ex) {
            }
        }
    }

    private void initPopupMenuTableEmailReaders() {
        JMenuItem miRestartAllEmailReaders = new JMenuItem(actionRestartAllEmailReaders);
        PopupMenuTableEmailReaders.add(miRestartAllEmailReaders);
        JMenuItem miDoCheckEmail = new JMenuItem(actionDoCheckEmails);
        PopupMenuTableEmailReaders.add(miDoCheckEmail);
    }

    private void initPopupMenuTableEmailMessages() {
        JMenuItem miMarkEmailMessagesAsSpam = new JMenuItem(actionMarkEmailMessagesAsSpam);
        PopupMenuTableEmailMessages.add(miMarkEmailMessagesAsSpam);
        JMenuItem miMarkEmailMessagesAsRead = new JMenuItem(actionMarkEmailMessagesAsRead);
        PopupMenuTableEmailMessages.add(miMarkEmailMessagesAsRead);
        JMenuItem miDeleteEmailMessages = new JMenuItem(actionDeleteEmailMessages);
        PopupMenuTableEmailMessages.add(miDeleteEmailMessages);
    }

    /*----------------------------------------------------------------------------
     * Table E-mail Messages
     */
    private final String ColMessageCount = "Qty.";
    private final int ColMessageCountWidth = 50;
    private final String ColSender = "Sender";
    private final String ColSubject = "Subject";
    private final String ColReceivedTime = "Received Time";
    private final String[] TableEmailMessagesHeaders = {ColRowNo, ColMessageCount, ColSender, ColSubject, ColReceivedTime};
    private final Class[] TableEmailMessagesHeaderClasses = {String.class, String.class, Object.class, null, Date.class};

    //<editor-fold defaultstate="collapsed" desc="table initialization">
    private void initTableEmailMessages() {
        tableEmailMessages.setModel(new javax.swing.table.DefaultTableModel(
                new Object[][]{}, TableEmailMessagesHeaders) {

            @Override
            public Class getColumnClass(int columnIndex) {
                final int ColSubjectIdx = MapColsTableEmailMessages.get(ColSubject);
                if (columnIndex == ColSubjectIdx) {
                    if (isGroupEmailsByConversation) {
                        return ItemGroupEmailMessages.class;
                    } else {
                        return ItemEmailMessage.class;
                    }
                } else {
                    return TableEmailMessagesHeaderClasses[columnIndex];
                }
            }

            @Override
            public boolean isCellEditable(int rowIndex, int columnIndex) {
                return false;
            }

            @Override
            public Object getValueAt(int row, int column) {
                final String ColName = getColumnName(column);

                if (ColName.equals(ColRowNo)) {
                    return String.format("%,02d", tableEmailMessages.convertRowIndexToView(row) + 1);
                } else if (ColName.equals(ColSubject)) {
                    return super.getValueAt(row, column);
                } else {
                    final int Col = MapColsTableEmailMessages.get(ColSubject);
                    ItemEmailMessage emailMessage = null;
                    if (isGroupEmailsByConversation) {
                        ItemGroupEmailMessages gem = (ItemGroupEmailMessages) super.getValueAt(row, Col);
                        emailMessage = gem.getNewestMessage();
                        if (ColName.equals(ColMessageCount)) {
                            return String.format("%,d", gem.getCount());
                        }
                    } else {
                        emailMessage = (ItemEmailMessage) super.getValueAt(row, Col);
                    }

                    if (emailMessage != null) {
                        if (ColName.equals(ColSender)) {
                            return emailMessage.getSender();
                        } else if (ColName.equals(ColReceivedTime)) {
                            return emailMessage.getReceivedDate();
                        }
                    }

                    return super.getValueAt(row, column);
                }
            }//getValueAt()
        });

        //------------------------ Init columns
        RightAlignCellRenderer rightAlignCellRenderer = new RightAlignCellRenderer();
        rightAlignCellRenderer.setCellHorzAlignment(SwingConstants.RIGHT);

        TableColumn col = tableEmailMessages.getColumn(ColRowNo);
        col.setCellRenderer(rightAlignCellRenderer);
        col.setMinWidth(50);
        col.setMaxWidth(50);

        col = tableEmailMessages.getColumn(ColMessageCount);
        col.setCellRenderer(rightAlignCellRenderer);
        col.setMinWidth(isGroupEmailsByConversation ? ColMessageCountWidth : 0);
        col.setMaxWidth(isGroupEmailsByConversation ? ColMessageCountWidth : 0);
        col.setPreferredWidth(isGroupEmailsByConversation ? ColMessageCountWidth : 0);

        col = tableEmailMessages.getColumn(ColReceivedTime);
        col.setCellRenderer(rightAlignCellRenderer);
        col.setMinWidth(200);
        col.setMaxWidth(200);

        //-------------------------- Listeners
        tableEmailMessages.getSelectionModel().addListSelectionListener(new ListSelectionListener() {

            public void valueChanged(ListSelectionEvent e) {
                if (!e.getValueIsAdjusting()) {
                    //TODO
                }
            }
        });

        //-------------------------------- row sorter
        DefaultRowSorter rowSorter = new TableRowSorter(tableEmailMessages.getModel());
        tableEmailMessages.setRowSorter(rowSorter);
        rowSorter.setComparator(tableEmailMessages.getColumn(ColSender).getModelIndex(), DefaultStringObjectComparator);
        rowSorter.setComparator(tableEmailMessages.getColumn(ColSubject).getModelIndex(), DefaultStringObjectComparator);

        //-------------------------------- popup menu
        tableEmailMessages.setComponentPopupMenu(PopupMenuTableEmailMessages);
        jScrollPane2.setComponentPopupMenu(PopupMenuTableEmailMessages);
    }// </editor-fold>

    private void updateTableEmailMessagesRowSorter() {
        RowSorter rowSorter = tableEmailMessages.getRowSorter();
        if (rowSorter == null) {
            return;
        }

        List sortKeys = rowSorter.getSortKeys();
        if (sortKeys == null || sortKeys.isEmpty()) {
            sortKeys = new ArrayList<RowSorter.SortKey>();
            sortKeys.add(new RowSorter.SortKey(
                    tableEmailMessages.getColumn(ColReceivedTime).getModelIndex(), SortOrder.DESCENDING));
            tableEmailMessages.getRowSorter().setSortKeys(sortKeys);
        }
    }

    private EmailReader getSelectedEmailReader() {
        if (tableEmailReaders.getSelectedRowCount() != 1) {
            return null;
        }

        final int Col = MapColsTableEmailReaders.get(ColEmailReader);
        return (EmailReader) tableEmailReaders.getValueAt(tableEmailReaders.getSelectedRow(), Col);
    }

    private synchronized void updateTableEmailMessages(EmailReader emailReader) {
        if (emailReader == null) {
            TableEmailMessagesModel.setRowCount(0);
            return;
        }

        if (isGroupEmailsByConversation) {
            List<ItemGroupEmailMessages> groupEmailMessages =
                    BeanUtils.groupEmailMessagesByConversation(emailReader.cloneEmailMessages());
            updateTableEmailMessageByGroup(groupEmailMessages);
        } else {
            List<ItemEmailMessage> emailMessages = emailReader.cloneEmailMessages();
            updateTableEmailMessageByMessage(emailMessages);
        }

        updateTableEmailMessagesRowSorter();
    }

    private void updateTableEmailMessageByGroup(List<ItemGroupEmailMessages> groupEmailMessages) {
        final int Col = MapColsTableEmailMessages.get(ColSubject);

        //remove old rows
        for (int iRow = TableEmailMessagesModel.getRowCount() - 1; iRow >= 0; iRow--) {
            ItemGroupEmailMessages group = (ItemGroupEmailMessages) TableEmailMessagesModel.getValueAt(iRow, Col);
            boolean found = false;
            for (ItemGroupEmailMessages newGroup : groupEmailMessages) {
                if (group.getNewestMessage().getSender().equals(newGroup.getNewestMessage().getSender())) {
                    TableEmailMessagesModel.setValueAt(newGroup, iRow, Col);
                    found = true;
                    break;
                }
            }
            if (!found) {
                TableEmailMessagesModel.removeRow(iRow);
            }
        }

        //add new rows
        for (int iNew = 0; iNew < groupEmailMessages.size(); iNew++) {
            ItemGroupEmailMessages newGroup = groupEmailMessages.get(iNew);
            boolean found = false;
            for (int iRow = TableEmailMessagesModel.getRowCount() - 1; iRow >= 0; iRow--) {
                ItemGroupEmailMessages group = (ItemGroupEmailMessages) TableEmailMessagesModel.getValueAt(iRow, Col);
                ItemEmailMessage groupNewestMessage = group.getNewestMessage();
                ItemEmailMessage newGroupNewestMessage = newGroup.getNewestMessage();
                if (groupNewestMessage.getSender().equals(newGroupNewestMessage.getSender())
                        && groupNewestMessage.getSubject().equals(newGroupNewestMessage.getSubject())) {
                    found = true;
                    break;
                }
            }
            if (!found) {
                Object[] row = new Object[TableEmailMessagesHeaders.length];
                row[Col] = newGroup;
                TableEmailMessagesModel.insertRow(iNew, row);
            }
        }

        TableEmailMessagesModel.fireTableDataChanged();
    }

    private void updateTableEmailMessageByMessage(List<ItemEmailMessage> emailMessages) {
        final int Col = MapColsTableEmailMessages.get(ColSubject);

        //remove old rows
        for (int iRow = TableEmailMessagesModel.getRowCount() - 1; iRow >= 0; iRow--) {
            ItemEmailMessage message = (ItemEmailMessage) TableEmailMessagesModel.getValueAt(iRow, Col);
            boolean found = false;
            for (ItemEmailMessage newMessage : emailMessages) {
                if (message.getID().equals(newMessage.getID())) {
                    found = true;
                    break;
                }
            }
            if (!found) {
                TableEmailMessagesModel.removeRow(iRow);
            }
        }

        //add new rows
        for (int iNew = 0; iNew < emailMessages.size(); iNew++) {
            ItemEmailMessage newMessage = emailMessages.get(iNew);
            boolean found = false;
            for (int iRow = TableEmailMessagesModel.getRowCount() - 1; iRow >= 0; iRow--) {
                ItemEmailMessage group = (ItemEmailMessage) TableEmailMessagesModel.getValueAt(iRow, Col);
                if (group.getID().equals(newMessage.getID())) {
                    found = true;
                    break;
                }
            }
            if (!found) {
                Object[] row = new Object[TableEmailMessagesHeaders.length];
                row[Col] = newMessage;
                TableEmailMessagesModel.insertRow(iNew, row);
            }
        }

        TableEmailMessagesModel.fireTableDataChanged();
    }

    /**
     * Shows the frame.
     */
    public void showPopupMessages() {
        PopupMessagesWindow.popup();
    }

    /**
     * Toggles...
     */
    public void togglePopupMessagesVisileStatus() {
        if (PopupMessagesWindow.isVisible()) {
            PopupMessagesWindow.setVisible(false);
            if (_SoundPlayer.isPlaying()) {
                _SoundPlayer.stop();
            }
        } else {
            showPopupMessages();
        }
    }

    /*----------------------------------------------------------------------------
     * popupMenuTableEmailReaders' actions
     */

    /*----------------------------------------------------------------------------
     * ActionRestartAll
     */
    private ActionRestartAllEmailReaders actionRestartAllEmailReaders;

    private class ActionRestartAllEmailReaders extends AbstractAction {

        public ActionRestartAllEmailReaders() {
            super("Restart All");
        }

        public void actionPerformed(ActionEvent e) {
            if (MessageBox.confirmYesNo(null, null, "Are you sure you want to restart all e-mail checker threads?", 1)) {
                restartProject();
            }
        }
    }

    /*----------------------------------------------------------------------------
     * ActionDoCheckEmail
     */
    private ActionDoCheckEmails actionDoCheckEmails;

    private class ActionDoCheckEmails extends AbstractAction {

        public ActionDoCheckEmails() {
            super("Check now");
        }

        public void actionPerformed(ActionEvent e) {
            if (tableEmailReaders.getSelectedRowCount() == 0) {
                MessageBox.showInfoMsg(null, null, "Please select e-mail account(s) to check");
            } else {
                final int Col = MapColsTableEmailReaders.get(ColEmailReader);
                for (int row = 0; row < tableEmailReaders.getRowCount(); row++) {
                    if (tableEmailReaders.isRowSelected(row)) {
                        EmailReader emailReader = (EmailReader) tableEmailReaders.getValueAt(row, Col);
                        emailReader.doForceCheck();
                    }
                }
            }
        }
    }

    /*----------------------------------------------------------------------------
     * popupMenuTableEmailMessages' actions
     */

    /*----------------------------------------------------------------------------
     * ActionMarkAsSpam
     */
    private ActionMarkEmailMessagesAsSpam actionMarkEmailMessagesAsSpam;

    private class ActionMarkEmailMessagesAsSpam extends AbstractAction {

        public ActionMarkEmailMessagesAsSpam() {
            super("Mark as spam");
        }

        public void actionPerformed(ActionEvent e) {
            MessageBox.showInfoMsg(null, null, "Under construction  :-)");
        }
    }

    /*----------------------------------------------------------------------------
     * ActionMarkAsRead
     */
    private ActionMarkEmailMessagesAsRead actionMarkEmailMessagesAsRead;

    private class ActionMarkEmailMessagesAsRead extends AbstractAction {

        public ActionMarkEmailMessagesAsRead() {
            super("Mark as read");
        }

        public void actionPerformed(ActionEvent e) {
            if (tableEmailMessages.getSelectedRowCount() <= 0) {
                MessageBox.showInfoMsg(null, null, "Please select e-mail messages to mark as read");
                return;
            }

            EmailReader selectedEmailReader = getSelectedEmailReader();
            if (selectedEmailReader == null) {
                return;
            }

            final int Col = MapColsTableEmailMessages.get(ColSubject);

            for (int row = 0; row < tableEmailMessages.getRowCount(); row++) {
                if (tableEmailMessages.isRowSelected(row)) {
                    if (isGroupEmailsByConversation) {
                        ItemGroupEmailMessages gem = (ItemGroupEmailMessages) tableEmailMessages.getValueAt(row, Col);
                        for (ItemEmailMessage emailMessage : gem) {
                            emailMessage.getFlags().put(Flag.SEEN, true);
                        }
                    } else {
                        ItemEmailMessage emailMessage = (ItemEmailMessage) tableEmailMessages.getValueAt(row, Col);
                        emailMessage.getFlags().put(Flag.SEEN, true);
                    }
                }
            }

            selectedEmailReader.doForceCheck();
        }
    }

    /*----------------------------------------------------------------------------
     * ActionDeleteEmail
     */
    private ActionDeleteEmailMessages actionDeleteEmailMessages;

    private class ActionDeleteEmailMessages extends AbstractAction {

        public ActionDeleteEmailMessages() {
            super("Delete");
        }

        public void actionPerformed(ActionEvent e) {
            if (tableEmailMessages.getSelectedRowCount() <= 0) {
                MessageBox.showInfoMsg(null, null, "Please select e-mail messages to delete");
                return;
            }
            EmailReader selectedEmailReader = getSelectedEmailReader();
            if (selectedEmailReader == null) {
                return;
            }

            if (!MessageBox.confirmYesNo(null, null, "Are you sure you want to delete selected message(s)?", 1)) {
                return;
            }

            final int Col = MapColsTableEmailMessages.get(ColSubject);

            for (int row = 0; row < tableEmailMessages.getRowCount(); row++) {
                if (tableEmailMessages.isRowSelected(row)) {
                    if (isGroupEmailsByConversation) {
                        ItemGroupEmailMessages gem = (ItemGroupEmailMessages) tableEmailMessages.getValueAt(row, Col);
                        for (ItemEmailMessage emailMessage : gem) {
                            emailMessage.getFlags().put(Flag.DELETED, true);
                        }
                    } else {
                        ItemEmailMessage emailMessage = (ItemEmailMessage) tableEmailMessages.getValueAt(row, Col);
                        emailMessage.getFlags().put(Flag.DELETED, true);
                    }
                }
            }

            selectedEmailReader.doForceCheck();
        }
    }
}
